/*
 * Copyright 2007 Brian Watkins
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package bw.cal;

import java.util.*;
import java.text.*;

import bw.util.*;
import bw.os.*;

public class CalendarEventManager
{
    public static final String EVENT_MGR_DAY_EVTS = "EVENT-MGR-DAY-EVENTS";
    public static final String EVENT_MGR_WEEK_EVTS = "EVENT-MGR-WEEK-EVENTS";
    public static final String EVENT_MGR_YEAR_EVTS = "EVENT-MGR-YEAR-EVENTS";

    private static CalendarEventManager _instance = null;
    private static Object _lock = new Object();

    private EventType _dayEvents = null;
    private EventType _weeklyEvents = null;
    private EventType _yearlyEvents = null;

    private SimpleDateFormat _dayEventFormat = new SimpleDateFormat( "yyyy-MM-dd" );
    private SimpleDateFormat _weekEventFormat = new SimpleDateFormat( "EEE" );
    private SimpleDateFormat _yearEventFormat = new SimpleDateFormat( "MM-dd" );

    private SimpleDateFormat _monthFormat = new SimpleDateFormat( "MM" );
    private SimpleDateFormat _monthYearFormat = new SimpleDateFormat( "yyyy-MM" );

    public static CalendarEventManager getInstance() 
    {
        if ( _instance == null )
        {
            synchronized ( _lock )
            {
                if ( _instance == null )
                {
                    _instance = new CalendarEventManager();
                }
            }
        }

        return _instance;
    }

    private CalendarEventManager ()
    {
        //create the event types
        //these objects take care of getting the actual event data from
        //the ObjectStore
        _dayEvents = new EventType( EVENT_MGR_DAY_EVTS, _dayEventFormat );
        _weeklyEvents = new EventType( EVENT_MGR_WEEK_EVTS, _weekEventFormat );
        _yearlyEvents = new EventType( EVENT_MGR_YEAR_EVTS, _yearEventFormat );
    }

    public void addEvent ( CalendarEvent evt )
    {
        getEventType( evt ).addEvent( evt );
    }

    public void removeEvent ( CalendarEvent evt )
    {
        getEventType( evt ).removeEvent( evt );
    }

    private EventType getEventType ( CalendarEvent evt )
    {
        EventType evtType = null;

        if ( CalendarEvent.DAY_EVENT == evt.getType() )
        {
            evtType = _dayEvents;
        }
        else if ( CalendarEvent.WEEKLY_EVENT == evt.getType() )
        {
            evtType = _weeklyEvents;
        }
        else if ( CalendarEvent.YEARLY_EVENT == evt.getType() )
        {
            evtType = _yearlyEvents;
        }

        return evtType;
    }
    
    public Vector getCalendarDates ( String dateStr, int numDays )
    {
        return getCalendarDates( parseDate( dateStr ), numDays );
    }

    public Vector getCalendarDates ( Date startDate, int numDays )
    {
        Vector calDates = new Vector();

        Date d = startDate;
        for ( int i = 0; i < numDays; i++ )
        {
            calDates.addElement( getCalendarDate( d ) );
            d = new Date( d.getTime() + 24*60*60*1000L );
        }

        return calDates;
    }

    public CalendarDate getCalendarDate ( String dateStr )
    {      
        return getCalendarDate( parseDate( dateStr ) );
    }

    public CalendarDate getCalendarDate ( Date date )
    {
        Vector events = new Vector();

        //get the events for this date in this year
        events.addAll( _dayEvents.getEventsForDate( date ) );

        //get the events for this day of the week
        events.addAll( _weeklyEvents.getEventsForDate( date ) );

        //get the events for this day of the year
        events.addAll( _yearlyEvents.getEventsForDate( date ) );

        CalendarDate d = new CalendarDate( date );
        d.setEvents( events );

        return d;
    }

    public Vector getAlertableEventsForMonth ( Date date )
    {
        Vector events = new Vector();

        //just get the daily and yearly events. skip the weekly schedule events
        events.addAll( _dayEvents.getEventsForMonth( _monthYearFormat.format( date ) ) );

        events.addAll( _yearlyEvents.getEventsForMonth( _monthFormat.format( date ) ) );

        return events;
    }

    private Date parseDate ( String dateStr )
    {
        Date d = null;

        try
        {
            d = _dayEvents.formatter.parse( dateStr );
        }
        catch ( ParseException ex )
        {
            //this should never happen since the date string will always be generated
            //behind the scenes but just in case ... 
            Log.getInstance().write( "Could not parse date string: " + dateStr, ex );
        }
        
        return d;
    }

    class EventType
    {
        String key = null;
        SimpleDateFormat formatter = null;

        public EventType ( String k, SimpleDateFormat f )
        {
            key = k;
            formatter = f;
        }

        private Hashtable events ()
        {
            Hashtable evts = null;
            try
            {
                evts = (Hashtable) ObjectStore.getInstance().get( key );                    
                if ( evts == null )
                {
                    evts = new Hashtable();
                    ObjectStore.getInstance().put( key, evts );
                }
            }
            catch ( Exception ex )
            {
                Log.getInstance().write( "Problem getting events!", ex );
                if ( evts == null ) { evts = new Hashtable(); } 
            }

            return evts;
        }

        public void addEvent ( CalendarEvent evt )
        {
            Hashtable events = this.events();

            String key = this.formatter.format( evt.getTime().getStart() );
            
            Vector v = (Vector) events.get( key );
            if ( v == null )
            {
                v = new Vector();
                events.put( key, v );
            }
            
            v.addElement( evt );

            //always store after any changes since this info is important
            this.store();
        }

        public void removeEvent ( CalendarEvent evt )
        {
            String key = this.formatter.format( evt.getTime().getStart() );
            Vector v = (Vector) this.events().get( key );
        
            v.removeElement( evt );

            //always store after any changes since don't want
            //to lose this info.
            this.store();
        }

        public Vector getEventsForDate ( Date date )
        {
            String key = this.formatter.format( date );
            
            Vector v = (Vector) this.events().get( key );
            if ( v == null )
            {
                v = new Vector();
            }
            
            return v;
        }

        public Vector getEventsForMonth ( String monthKey )
        {
            Vector v = new Vector();
            
            Hashtable events = this.events();

            for ( Enumeration e = events.keys(); e.hasMoreElements(); )
            {
                String key = (String) e.nextElement();
                if ( key.startsWith( monthKey ) )
                {
                    v.addAll( (Vector) events.get( key ) );
                }
            }
            
            return v;
        }

        private void store ()
        {
            try
            {
                ObjectStore.getInstance().put( this.key, this.events() );
            }
            catch ( Exception ex )
            {
                Log.getInstance().write( "Problem storing " + this.key, ex );
            }
        }

    }

}